package cn.kennylee.codehub.mq.lip.integration.rocketmq;

import cn.hutool.crypto.digest.DigestUtil;
import cn.kennylee.codehub.mq.lip.integration.base.AbstractMqListener;
import cn.kennylee.codehub.mq.lip.integration.base.IntegrationMqConsumerAnnoProcessor;
import cn.kennylee.codehub.mq.lip.integration.base.anno.IntegrationMqConsumer;
import cn.kennylee.codehub.mq.lip.integration.base.anno.MqListenerGenerator;
import cn.kennylee.codehub.mq.lip.integration.rocketmq.listener.AbstractRocketMqListener;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.jar.asm.Label;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.matcher.ElementMatchers;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * RocketMQ消息监听器生成器
 * <p>Created on 2025/4/18.</p>
 *
 * @author kennylee
 * @since 0.0.1
 */
@Slf4j
public class RocketMqConsumerGenerator implements MqListenerGenerator {
    /**
     * 缓存的RocketMQMessageListener实现类
     */
    private static final ConcurrentMap<String, Class<?>> CONSUMER_CLASS_CACHE = new ConcurrentHashMap<>();

    @Override
    public IntegrationMqConsumer.MqType supportType() {
        return IntegrationMqConsumer.MqType.ROCKET_MQ;
    }

    @Override
    public AbstractMqListener<?> generateListener(Object targetBean, Method targetMethod, IntegrationMqConsumer annotation) {
        final String className = IntegrationMqConsumerAnnoProcessor.generateClassName(targetBean, targetMethod);

        // 生成RocketMQMessageListener实现类
        log.info("生成RocketMQMessageListener实现类，className = {}", className);

        // 避免特殊字符导致的缓存key不合法，生成一个MD5值作为缓存key
        final String cacheKey = DigestUtil.md5Hex(className);

        // 获取bean的原始class
        Class<?> originClass = IntegrationMqConsumerAnnoProcessor.getTargetClass(targetBean);

        Class<?> targetClass = CONSUMER_CLASS_CACHE.computeIfAbsent(cacheKey, k -> {
            try (DynamicType.Unloaded<AbstractRocketMqListener> rocketMqListenerUnloaded = new ByteBuddy()
                .subclass(AbstractRocketMqListener.class)
                .name(className)
                // 定义构造函数：无参，但调用 super(CHANNEL, ROUTER_KEY)
                .defineConstructor(Visibility.PUBLIC)
                .intercept(MethodCall
                    .invoke(AbstractRocketMqListener.class.getConstructor(String.class, String[].class))
                    .with(annotation.channel(), annotation.routerKeys())
                )
                .defineField("target", originClass, Visibility.PRIVATE)
                .defineField("method", Method.class, Visibility.PRIVATE)
                .method(ElementMatchers.named("processMessage"))
                // 定义实现/拦截目标方法
                .intercept(newImplementation(targetBean, targetMethod))
                .make()) {

                return rocketMqListenerUnloaded
                    .load(originClass.getClassLoader())
                    .getLoaded();
            } catch (Exception e) {
                log.error("生成RocketMQMessageListener实现类失败，className = {}", className, e);
                throw new RuntimeException(e);
            }
        });

        try {
            AbstractMqListener<?> abstractMqListener = (AbstractMqListener<?>) targetClass.getDeclaredConstructor().newInstance();

            Field targetField = targetClass.getDeclaredField("target");
            Field methodField = targetClass.getDeclaredField("method");
            targetField.setAccessible(true);
            methodField.setAccessible(true);
            targetField.set(abstractMqListener, targetBean);
            methodField.set(abstractMqListener, targetMethod);

            // 设置注解中的属性
            if (StringUtils.hasLength(annotation.consumerGroup())) {
                abstractMqListener.setConsumerGroup(annotation.consumerGroup());
            }
            abstractMqListener.setEnableTagSuffix(annotation.enableTagSuffix());
            abstractMqListener.setEnableGroupSuffix(annotation.enableGroupSuffix());
            abstractMqListener.setConsumeThreadNumber(annotation.consumeThreadNumber());
            abstractMqListener.setConsumeThreadNumberHolder(annotation.consumeThreadNumberHolder());

            return abstractMqListener;
        } catch (InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchFieldException |
                 NoSuchMethodException e) {
            log.error("生成RocketMQMessageListener实现类失败，className = {}", className, e);
            throw new RuntimeException(e);
        }
    }

    @NonNull
    private static Implementation newImplementation(Object targetBean, Method targetMethod) {
        return new Implementation() {
            @NonNull
            @Override
            public InstrumentedType prepare(@NonNull InstrumentedType instrumentedType) {
                return instrumentedType;
            }

            @NonNull
            @Override
            public ByteCodeAppender appender(@NonNull Target implementationTarget) {
                return (methodVisitor, implementationContext, instrumentedMethod) -> {

                    Class<?> returnType = targetMethod.getReturnType();

                    // 1. 参数校验
                    if (!void.class.equals(returnType) && !boolean.class.equals(returnType)) {
                        throw new IllegalArgumentException("@IntegrationMqConsumer 方法必须返回 void 或 boolean 类型");
                    }

                    // 2. 获取目标方法参数信息
                    Class<?>[] parameterTypes = targetMethod.getParameterTypes();
                    int targetParamCount = parameterTypes.length;

                    // 3. 计算最大局部变量数
                    int maxLocals = 2; // this(0) + MessageExt参数(1)
                    if (targetParamCount > 1) {
                        maxLocals = 3; // 如果需要处理第二个参数
                    }

                    // 4. 开始生成字节码
                    methodVisitor.visitLabel(new Label());

                    // 5. 加载 target 对象
                    methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                    methodVisitor.visitFieldInsn(Opcodes.GETFIELD,
                        implementationContext.getInstrumentedType().getInternalName(),
                        "target",
                        "L" + IntegrationMqConsumerAnnoProcessor.getTargetClass(targetBean).getName().replace('.', '/') + ";");

                    // 6. 加载 Method 对象
                    methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
                    methodVisitor.visitFieldInsn(Opcodes.GETFIELD,
                        implementationContext.getInstrumentedType().getInternalName(),
                        "method",
                        "Ljava/lang/reflect/Method;");

                    // 6. 交换栈顶元素（target和method交换位置）
                    methodVisitor.visitInsn(Opcodes.SWAP);

                    // 7. 创建参数数组
                    if (targetParamCount <= 5) {
                        methodVisitor.visitInsn(Opcodes.ICONST_0 + targetParamCount);
                    } else {
                        methodVisitor.visitIntInsn(Opcodes.BIPUSH, targetParamCount);
                    }
                    methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");

                    // 9. 填充参数数组
                    for (int i = 0; i < targetParamCount; i++) {
                        methodVisitor.visitInsn(Opcodes.DUP); // duplicate array reference

                        // 加载数组索引
                        if (i <= 5) {
                            methodVisitor.visitInsn(Opcodes.ICONST_0 + i);
                        } else {
                            methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                        }

                        // 加载对应参数
                        if (i == 0) {
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, 1); // MessageVo
                        } else {
                            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); // this
                        }

                        methodVisitor.visitInsn(Opcodes.AASTORE);
                    }

                    // 10. 调用 Method.invoke()
                    methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
                        "java/lang/reflect/Method",
                        "invoke",
                        "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
                        false);

                    // 11. 处理返回值
                    if (void.class.equals(returnType)) {
                        methodVisitor.visitInsn(Opcodes.POP);
                        methodVisitor.visitInsn(Opcodes.ICONST_1); // void方法默认返回true
                    } else {
                        methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
                        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
                            "java/lang/Boolean",
                            "booleanValue",
                            "()Z",
                            false);
                    }

                    methodVisitor.visitInsn(Opcodes.IRETURN);

                    return new ByteCodeAppender.Size(8, maxLocals);
                };
            }
        };
    }
}
